9-1 讀gp瘞?

一般而言,客戶端的 JavaScript 不能對客戶端的硬碟做任何存取的動作,此限制的目的是為了要保護客戶端電腦的硬碟資料,避免有惡意的 JavaScript 程式碼來對客戶端電腦或資料進行破壞的動作。唯一的例外,就是 JavaScript 可以在客戶端的硬碟存取極少量的有限資料,這些資料稱為小餅乾(Cookie),大部分都是和用戶相關的個人資料,常見的相關應用如下: 使用 Cookie 來記錄資料的好處可以列舉如下:
  1. 所有資料均存放在客戶端電腦,不會佔用伺服器硬碟空間。
  2. 與 Cookie 相關的運算均在客戶端電腦進行,不會增加伺服器運算負載。
  3. 簡單易用,可以使用客戶端的 JavaScript 或伺服器端的 ASP 即可對 Cookie 進行讀寫。
但對於網頁程式設計師來說, Cookie 的使用也有一些不盡理想之處:
  1. 不可靠,因客戶端可以完全關閉 Cookie 的功能,此時 JavaScript 與 Cookie 相關的程式碼就無法運作。(此時必須先檢測 Cookie 功能是否被關閉,再跳到不同的程式片段,因而造成程式碼的複雜。)
  2. 客戶換用不同品牌的瀏覽器時,就無法抓到由原來瀏覽器所寫入的 Cookie 資訊。
  3. 客戶重灌電腦時,就會造成 Cookie 資訊的流失。
  4. 客戶換台電腦時,Cookie 的資訊就無法帶到另一台電腦。
雖然有上述的缺點,但是 Cookie 的使用還是很普遍,因為目前大部分的瀏覽器都支援 Cookie,而且一般使用者也沒有必要去關閉 Cookie 的功能。

首先我們看看如何檢查瀏覽器是否開啟 Cookie 功能,這可以經由 navigator.cookieEnabled 來判斷,請見下列範例:

Example(cookie01.htm):

上述範例的原始檔如下:

原始檔(cookie01.htm):(灰色區域按兩下即可拷貝)
<html>
<head>
<meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=big5">
</head>

<body>
<h2 align=center>檢查瀏覽器的 Cookie 功能是否啟動</h2>
<hr>

<script>
document.write("navigator.cookieEnabled = " + navigator.cookieEnabled + "<br>");
if (navigator.cookieEnabled){
	document.write("Cookie 功能已經啟動!");
	// 在此加入使用 Cookie 的程式碼
} else {
	document.write("Cookie 功能尚未啟動!");
	alert("你的瀏覽器設定不支援 Cookie,請先開啟瀏覽器的 Cookie 功能後,才能得到瀏覽本網頁的最佳效果!");
	// 在此加入不使用 Cookie 的程式碼
}
</script>

<hr>
</body>
</html>

由此可知,我們可以使用 navigator.cookieEnabled 來判斷客戶端的瀏覽器是否開啟 Cookie 的功能,並進而選用不同的程式碼來達到網頁的既定功能。

Hint
若要關閉 Cookie 的功能,在 IE 瀏覽器可以經由下列下拉式選單來修改:工具/網際網路選項/隱私權。請關閉 Cookie 功能後,再檢測 navigator.cookieEnabled 的值,是否如你想像?為什麼?

完整的 Cookie 是以下列字串形式存放在客戶端的硬碟:

name=value;expires=expDate;
其中 name 代表 Cookie 的名稱,value 則是對應的 Cookie 值,expDate 代表 Cookie 的有效期間,若超過此時間,Cookie 就會被刪除。若沒有指定有效時間,則 Cookie 只會被儲存在記憶體中,在使用者關掉所有的瀏覽器後,或在 session 逾時(Session time-out,預設值通常是 20 分鐘)後,Cookie 就會被自動刪除了。

對於任一個網頁而言,Cookie 是一個字串,存放在 document.cookie 字串之中,我們可以使用下列範例來印出 Cookie 字串的值:

Example(cookie011.htm):

在上述範例中,出現一個似乎由亂數產生的 name/value pair,這是 ASP 的 session 變數,是由微軟 IIS Web 伺服器自動設定的資訊,用以追蹤每個使用者的使用習慣。上述範例的原始檔如下:

原始檔(cookie011.htm):(灰色區域按兩下即可拷貝)
<html>
<head>
<meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=big5">
</head>

<body>
<h2 align=center>列出 Cookie 字串</h2>
<hr>

本頁的 Cookie 字串是:
<script>
document.write(document.cookie);
</script>

<hr>
</body>
</html>

若要設定 Cookie,可見下列範例:

Example(cookie02.htm):

上述範例的原始檔如下:

原始檔(cookie02.htm):(灰色區域按兩下即可拷貝)
<html>
<head>
<meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=big5">
</head>

<body>
<h2 align=center>設定並顯示 Cookie 的範例</h2>
<hr>

<script src="cookieUtility.js"></script>
<form>
請設定小餅乾的 name-value pair:<br>
Name: <input id=cookieName><br>
Value: <input id=cookieValue><br>
<input type=button value="寫入 Cookie"
	onClick="document.cookie=escape(cookieName.value)+'='+escape(cookieValue.value); history.go(0)">
<p>請注意:這些小餅乾只存放在記憶體中,會隨著所有瀏覽器的關閉或 Session 逾時而消失。
</form>

<hr>
<h3 align=center>本網頁的小餅乾列表</h3>
<script>listCookie();</script>

<hr>
</body>
</html>

在上述範例中,我們直接將 document.cookie 設定為 name+value 的形式,就可以加入一個 Cookie(或是 name/value pair)。

Hint
當我們使用 document.cookie="aaa=bbb" 時,JavaScript 並不會蓋掉原先就有的 Cookie 資料,而是將相關資料加到 document.cookie 的尾端,這是要特別注意之處。

為避開空格或其他可能造成錯誤之字元,在存取 name 或 value 時,最好使用 escape() 及 unescape() 函數來進行編碼與解碼,以便避掉具有特殊意義的字元。以下是一個使用 escape() 的範例:

Example(escape01.htm):

上述範例的原始檔如下:

原始檔(escape01.htm):(灰色區域按兩下即可拷貝)
<html>
<head>
<meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=big5">
</head>

<body>
<h2 align=center>使用 escape() 來編碼</h2>
<hr>

<form>
編碼前字串:<input id=source value="x y z"><br>
<input type=button value="進行編碼" onClick="target.value=escape(source.value)"><br>
編碼後字串:<input id=target>
</form>

<hr>
</body>
</html>

在上述範例中,空格被轉換成「%20」,其作法是先將空格轉成 ASCII 碼,也就是 32,然後再將 32 轉成16進位,再加上百分比,就得到編碼後的「%20」。

Hint
若使用 escape() 對中文進行編碼,其結果是以 unicode 來表示的 16 進位字串。例如「編」會被轉成「%u7DE8」,其中 7DE8 就是「編」的 unicode 的 16 進位表示法。

此外,在 cookie02.htm 列出所有的 Cookies 時,上述範例呼叫了一個函數 listCookie(),此函數是定義在 cookieUtility.js 之中。事實上,cookieUtility.js 包含了數種常用的基本函數,可以對 Cookie 進行各種處理,例如:

對於常處理 Cookie 的程式設計師而言,這些函式都會常常用到,原始碼如下:

原始檔(cookieUtility.js):(灰色區域按兩下即可拷貝)
// 與 cookie 相關的各種函數
// Set cookie
function setCookie(name, value) {
	var argv = setCookie.arguments;
	var argc = setCookie.arguments.length;
	var expires = (argc > 2) ? argv[2] : null;
	var path = (argc > 3) ? argv[3] : null;
	var domain = (argc > 4) ? argv[4] : null;
	var secure = (argc > 5) ? argv[5] : null;

	document.cookie = escape(name) + "=" + escape(value) +
	((expires == null) ? "" : ("; expires=" + expires.toGMTString())) +
	((path == null) ? "" : ("; path=" + path)) +
	((domain == null) ? "" : ("; domain=" + domain)) +
	((secure == null) ? "" : ("; secure=" + secure));
}

// Delete cookie entry
function delCookie(name) {
	var expDate = new Date();
	expDate.setTime(expDate.getTime()-1);	// 設定 Cookie 的失效時間比目前時間還早
	document.cookie = escape(name) + "=; expires=" + expDate.toGMTString();	// 重新設定 Cookie
//	alert(document.cookie);
}

// Get cookie by name
function getCookie(name) {
	var arg = escape(name) + "=";
	var nameLen = arg.length;
	var cookieLen = document.cookie.length;
	var i = 0;
   
	while (i < cookieLen) {
		var j = i + nameLen;
		if (document.cookie.substring(i, j) == arg)
			return getCookieValueByIndex(j);
		i = document.cookie.indexOf(" ", i) + 1;
		if (i == 0) break;
	}
	return null;
}

// Show the cookie string
function showAllCookie() {
	alert(document.cookie);
}

function getCookieValueByIndex(startIndex) {
	var endIndex = document.cookie.indexOf(";", startIndex);
	if (endIndex == -1) 
		endIndex = document.cookie.length;
	return unescape(document.cookie.substring(startIndex, endIndex));
}

// List all name/value pairs in a table
function listCookie() {
	document.writeln("<p><b>原始 Cookie 字串:<p></b><center><font color=green>" + document.cookie + "</font></center>");
	document.writeln("<p><b>拆解後的 name/value:</b>");
	document.writeln("<table border=1 align=center>");
	document.writeln("<tr><th>Name<th>Value");
	cookieArray = document.cookie.split(";");
	for (var i=0; i<cookieArray.length; i++) {
		thisCookie = cookieArray[i].split("=");
		cookieName = unescape(thisCookie[0]);
		cookieValue = unescape(thisCookie[1]);
		document.writeln("<tr><td><font color=red>"+cookieName+"</font><td><font color=green>"+cookieValue+"</font>");
	}
	document.writeln("</table>");
}

// Delete all cookies
function deleteAllCookie() {
	var cookieArray = document.cookie.split(";");
	for (var i=0; i<cookieArray.length; i++) {
		thisCookie = cookieArray[i].split("=");
		cookieName = unescape(thisCookie[0]);
		delCookie(cookieName); 
	}
}


JavaScript 程式設計與應用:用於網頁用戶端